package net

import (
	"bufio"
	"corelib/log"
	"corelib/parse"
	"errors"
	"net"
	"sync"
)

type AppTcp struct {
	addr        string
	acceptCheck OptSession // 用于IP黑名单检测
	msgHandle   func(body []byte, w Conn)
	beforeMsg   []OptSession        // 用户处理消息前的会话验证 eg 1.验证token 2.确认区服
	sessionMap  map[string]*Session //  addr->Session
	idMap       map[any]string      // id->addr
	mapMutx     sync.RWMutex
}

type OptSession func(s *Session) bool

type Session struct {
	fd          net.Conn
	reader      *bufio.Reader
	laddr       string
	raddr       string
	identifier  any // 标识符
	onClose     func(s *Session)
	closeOnce   sync.Once
	onMarkId    func(id any, addr string)
	onMsgHandle func(body []byte, w Conn)
}

func (s *Session) Close() {
	s.closeOnce.Do(func() {
		log.Printf("session [%s] [identifier=%v] close", s.raddr, s.identifier)
		_ = s.fd.Close()
		if s.onClose != nil {
			s.onClose(s)
		}
	})
}

func (s *Session) onMsg() {
	go func() {
		defer func() {
			if p := recover(); p != nil {
				log.Error("connect excepiton:%v", p)
			}
			log.Print("session onMsg over")
			s.Close()
		}()
		for {
			msg, err := s.Recv()
			if err == nil {
				log.Info("session %v recv msg:[%s]", s.identifier, msg)
				if len(msg) > 0 && s.onMsgHandle != nil {
					s.onMsgHandle(msg, s)
				}
			} else {
				log.Error("session read msg failed err:%v", err)
				break
			}
		}
	}()
}
func (s *Session) GetRaddr() string {
	return s.raddr
}
func (s *Session) GetConn() net.Conn {
	return s.fd
}
func (s *Session) MarkId(id any) {
	s.identifier = id
	if s.onMarkId != nil {
		s.onMarkId(id, s.raddr)
	}
}

func (s *Session) Recv() ([]byte, error) {
	return parse.MsgUnpackDemo(s.reader)
}

func (s *Session) Send(b []byte) (n int, err error) {
	return s.fd.Write(parse.MsgPackDemo(b))
}

func (a *AppTcp) RegisterRouter(routers ...RouterInterface) {

}

func (a *AppTcp) SetBeforeOnMsg(f ...OptSession) {
	a.beforeMsg = append(a.beforeMsg, f...)
}

func (a *AppTcp) Start() error {
	listener, err := net.Listen("tcp", a.addr)
	if err != nil {
		return err
	}
	defer listener.Close()
	log.Printf("start tcp listening... on addr:%v", listener.Addr())
	for {
		conn, er := listener.Accept()
		if er != nil {
			log.Printf("")
		}
		raddr := conn.RemoteAddr().String()
		log.Printf("recv connect raddr:%v", raddr)
		s := &Session{
			fd:        conn,
			reader:    bufio.NewReader(conn),
			raddr:     raddr,
			closeOnce: sync.Once{},
			onClose: func(s *Session) {
				a.mapMutx.Lock()
				defer a.mapMutx.Unlock()
				delete(a.sessionMap, s.raddr)
				if s.identifier != "" {
					delete(a.idMap, s.identifier)
				}
			},
			onMarkId: func(id any, addr string) {
				a.mapMutx.Lock()
				a.idMap[id] = addr
				a.mapMutx.Unlock()
			},
			onMsgHandle: a.msgHandle,
		}
		{
			a.mapMutx.Lock()
			a.sessionMap[s.raddr] = s
			a.mapMutx.Unlock()
		}
		if a.acceptCheck != nil && a.acceptCheck(s) == false {
			s.Close()
		} else {
			if len(a.beforeMsg) > 0 {
				for _, f := range a.beforeMsg {
					if false == f(s) {
						s.Close()
						return errors.New("connect beforeMsg check failed")
					}
				}
			}
			s.onMsg()
		}
	}
}

func (a *AppTcp) SetAcceptCheck(f func(s *Session) bool) {
	a.acceptCheck = f
}

func (a *AppTcp) SetMsgHandle(f func(body []byte, w Conn)) {
	a.msgHandle = f
}

func NewTcpSer(addr string) *AppTcp {
	app := &AppTcp{
		addr:       addr,
		sessionMap: make(map[string]*Session),
		idMap:      make(map[any]string),
		mapMutx:    sync.RWMutex{},
	}
	return app
}
